Skip to content

lib,test_runner: fix styleText colorizing non-TTY streams under --test - #65558

Open
piyushrajyadav wants to merge 1 commit into
nodejs:mainfrom
piyushrajyadav:fix-styletext-istty-force-color
Open

lib,test_runner: fix styleText colorizing non-TTY streams under --test#65558
piyushrajyadav wants to merge 1 commit into
nodejs:mainfrom
piyushrajyadav:fix-styletext-istty-force-color

Conversation

@piyushrajyadav

@piyushrajyadav piyushrajyadav commented Aug 26, 2026

Copy link
Copy Markdown

Summary

Fixes: #57921
Refs: #57935 (previous attempt, closed stale)

Problem

When
ode --test runs files in process-isolation mode it sets FORCE_COLOR=1 in the child's environment to carry test-reporter colour through. A side-effect is that util.styleText() in user code inside the child colorises any stream β€” even those with isTTY = false β€” because shouldColorize() short-circuits to getColorDepth() as soon as FORCE_COLOR is defined, ignoring the caller's stream entirely.

js // With node --test, FORCE_COLOR=1 is injected into the child env. // Both of these then return ANSI-styled text, even though streamNoTTY.isTTY = false: styleText('bgYellow', 'TTY', { stream: streamTTY }); // ← correct styleText('bgYellow', 'No TTY', { stream: streamNoTTY }); // ← WRONG (bug)

Approach

Previous PR #57935 explored a few directions. cjihrig suggested not hacking FORCE_COLOR at all and using NODE_TEST_CONTEXT (the existing test-runner-internal env var) as a communication channel instead.

This PR implements exactly that:

lib/internal/test_runner/runner.js

  • Remove env.FORCE_COLOR = '1'
  • Add NODE_TEST_CONTEXT as a JSON string: { context: 'child-v8', colorize: }

lib/internal/util/colors.js β€” shouldColorize() priority order

  1. FORCE_COLOR β€” user-controlled, unchanged, highest priority
  2. stream.isTTY β€” stream capability check, unchanged
  3. NODE_TEST_CONTEXT.colorize β€” new last-resort fallback, only used when neither of the above is truthy

js shouldColorize(stream) { if (process.env.FORCE_COLOR !== undefined) { return lazyInternalTTY().getColorDepth() > 2; } if (stream?.isTTY) { return typeof stream.getColorDepth === 'function' ? stream.getColorDepth() > 2 : true; } // Last resort: test-runner internal colorize flag const ctx = getTestContext(); return ctx?.colorize === true && lazyInternalTTY().getColorDepth() > 2; },

lib/internal/test_runner/utils.js

  • Updated isChildProcessV8 detection to handle both the legacy plain-string 'child-v8' and the new JSON { context: 'child-v8' } forms.

Testing

  • Existing test cases in est/parallel/test-util-styletext.js retained
  • Added regression cases exercising NODE_TEST_CONTEXT.colorize for isTTY=false streams

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. util Issues and PRs related to the built-in util module. labels Aug 26, 2026
When
ode --test runs files in process-isolation mode it used to set
FORCE_COLOR=1 in the child's environment so that test-runner output
arrives pre-colored. A side-effect was that util.styleText() inside
the child process would colorize *any* stream, even those with an
explicit isTTY=false, because shouldColorize() short-circuits to
getColorDepth() as soon as FORCE_COLOR is defined.

Fix: stop setting FORCE_COLOR in the child environment. Instead, encode
the parent's colorize decision as a JSON field in NODE_TEST_CONTEXT
(the existing internal-only variable that child processes already use
to identify themselves as test-runner workers):

  NODE_TEST_CONTEXT = JSON.stringify({ context: 'child-v8', colorize: <bool> })

In lib/internal/util/colors.js shouldColorize() is updated to use that
field as a *last-resort* fallback when neither FORCE_COLOR nor
stream.isTTY is truthy. The priority order is now:

  1. FORCE_COLOR (user-controlled, highest priority)
  2. stream.isTTY / stream.getColorDepth()
  3. NODE_TEST_CONTEXT.colorize (test-runner internal, last resort)

lib/internal/test_runner/utils.js and runner.js are updated to parse
the JSON form of NODE_TEST_CONTEXT alongside the legacy plain-string
values ('child' / 'child-v8') for backwards compatibility.

Fixes: nodejs#57921
Refs: nodejs#57935

Signed-off-by: piyushrajyadav <piyushyadavrajyadav@gmail.com>
@piyushrajyadav
piyushrajyadav force-pushed the fix-styletext-istty-force-color branch from 9229f1f to d734d0c Compare August 26, 2026 08:05
@piyushrajyadav piyushrajyadav changed the title lib: respect stream.isTTY=false in styleText even when FORCE_COLOR is set lib,test_runner: fix styleText colorizing non-TTY streams under --test Aug 26, 2026
@piyushrajyadav

Copy link
Copy Markdown
Author

Could anyone with access please trigger the Jenkins CI for this PR when convenient? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. util Issues and PRs related to the built-in util module.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

styleText(): isTTY check fails with --test

2 participants